﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;
using Kokomo.Lexical.Coding;
using Kokomo.Lexical.Expressions;
using CodeLabel = Kokomo.Lexical.Coding.Label;
using System.Diagnostics;
using Kokomo.PeInspector.Reflection.Disassembly.Flow;
using Kokomo.Mirror.CliMetadata;

namespace Kokomo.PeInspector.Reflection.Disassembly
{
	class MsilDecompilationContext : DecompilationContext
	{
		public MsilDecompilationContext(
			MethodBase method,
			IEnumerable<Instruction> instructions,
			Action<MsilDecompilationContext> opCodeHandler
			)
		{
			if (method == null) throw new ArgumentNullException("method");
			if (instructions == null) throw new ArgumentNullException("instructions");
			if (opCodeHandler == null) throw new ArgumentNullException("opCodeHandler");

			this.Method = method;
			this.Instructions = instructions;
			this.Transitions = new List<MsilTransition>();
			this.OpCodeHandler = opCodeHandler;

			this.ExpressionStack = new Stack<MsilStackEntry>();
			this.Parameters = method.GetParameters();
			this.MethodBody = method.GetMethodBody();
			this.MethodLength = this.MethodBody.GetILAsByteArray().Length;

			this.StatementFrameTable = new Dictionary<Statement, MsilFrameInfo>();
			this.StatementLocalVariableReferences = new List<LocalVariable>();
			this.VariableReferences = new List<MsilVariableReference>();
			this.DeclaredVariables = new List<Variable>();
			this.GhostedAssignmentTable = new Dictionary<ISettableExpression, GhostAssignment>();
			this.DecisionValueTable = new Dictionary<LocalVariable, Expression>();
			this.AnonymousMethodDelegateTable = new Dictionary<FieldInfo, Expression>(MemberEqualityComparer.Instance);

			this.InitializeLocalVariables();
			this.InitializeParameters();

			this._blockStartOffsets = new List<int>();
			this.Blocks = new List<MsilBlock>();

			this.InstructionEnumerator = this.Instructions.GetEnumerator();

			this.LabelTable = new CachedLookupTable<int, CodeLabel>(this.CreateLabel);

			this.LinqParameterTable = new Dictionary<LocalVariable, TypedVariable>();
		}

		public override string ToString()
		{
			return string.Format("{0}::{1}", this.Method.DeclaringType.FullName, this.Method);
		}

		public MethodBase Method { get; private set; }
		public ParameterInfo[] Parameters { get; private set; }
		public MethodBody MethodBody { get; private set; }
		public int MethodLength { get; private set; }
		public Action<MsilDecompilationContext> OpCodeHandler { get; private set; }

		#region Equality
		private bool Equal(MemberInfo x, MemberInfo y)
		{
			return MemberEqualityComparer.Instance.Equals(x, y);
		}
		#endregion
		#region Instructions
		public IEnumerable<Instruction> Instructions { get; private set; }
		public Instruction GetInstructionAt(int offset)
		{
			return this.Instructions.Single(instruction => instruction.Offset == offset);
		}
		public Instruction GetInstructionJustBefore(int offset)
		{
			if (offset == 0) return null;
			return this.Instructions.Single(record => record.NextOffset == offset);
		}

		public Instruction CurrentInstruction { get { return this.InstructionEnumerator.Current; } }
		public int CurrentOffset { get { return this.CurrentInstruction.Offset; } }
		private IEnumerator<Instruction> InstructionEnumerator { get; set; }

		public bool Advance()
		{
			bool result = this.InstructionEnumerator.MoveNext();

			return result;
		}
		#endregion
		#region Variables
		public List<MethodArgumentVariable> ParameterVariables { get; private set; }
		private void InitializeParameters()
		{
			List<MethodArgumentVariable> parameters = new List<MethodArgumentVariable>();
			foreach (var argumentInfo in this.Parameters)
			{
				MethodArgumentVariable variable = new MethodArgumentVariable(argumentInfo);
				parameters.Add(variable);
			}
			this.ParameterVariables = parameters;
		}
		public List<LocalVariable> LocalVariables { get; private set; }
		private void InitializeLocalVariables()
		{
			MethodBody body = this.Method.GetMethodBody();

			List<LocalVariable> locals = new List<LocalVariable>();
			foreach (var localInfo in body.LocalVariables)
			{
				string name = "$var" + locals.Count;
				LocalVariable variable = new LocalVariable(name, localInfo);
				locals.Add(variable);
			}
			this.LocalVariables = locals;
		}

		public List<MsilVariableReference> VariableReferences { get; private set; }
		public bool IsVariableReferencedWithin(
			Variable variable,
			int blockStartOffset,
			int blockEndOffset
			)
		{
			bool referenced = this.VariableReferences.Exists(
				r =>
					(r.Variable == variable)
					&& (r.Offset >= blockStartOffset)
					&& (r.Offset < blockEndOffset)
					);
			return referenced;
		}
		public bool IsVariableReferencedOutsideOf(
			Variable variable,
			int blockStartOffset,
			int blockEndOffset
			)
		{
			bool referenced = this.VariableReferences.Exists(
				r =>
					(r.Variable == variable)
					&& (
						(r.Offset < blockStartOffset)
						|| (r.Offset >= blockEndOffset)
						)
					);
			return referenced;
		}
		//public void RecordVariableReference(
		//    Variable variable,
		//    MsilVariableReferenceType type
		//    )
		//{
		//    if (variable == null) throw new ArgumentNullException("variable");

		//    MsilVariableReference reference = new MsilVariableReference(variable, this.CurrentOffset, type);
		//    //this.VariableReferences.Add(reference);
		//    this.StatementLocalVariableReferences.Add(reference);
		//}
		public MsilVariableReference FindNextVariableReference(int offset, Variable variable)
		{
			if (variable == null) throw new ArgumentNullException("variable");

			MsilVariableReference variableRef = this.VariableReferences
				.Where(reference =>
					(reference.Offset > offset)
					&& (reference.Variable == variable)
					)
				.OrderBy(reference => reference.Offset)
				.FirstOrDefault()
				;

			return variableRef;
		}
		public bool IsVariableReferencedAfter(int offset, Variable variable, MsilVariableReferenceType referenceType)
		{
			bool isReferenced = this.VariableReferences.Exists(
				reference =>
					(reference.Offset > offset)
					&& (reference.Variable == variable)
					&& (reference.Type == referenceType)
					);

			return isReferenced;
		}

		public List<Variable> DeclaredVariables { get; private set; }

		protected Dictionary<ISettableExpression, GhostAssignment> GhostedAssignmentTable { get; private set; }
		public void GhostAssign(ISettableExpression settable, Expression value)
		{
			if (settable == null) throw new ArgumentNullException("settable");
			if (value == null) throw new ArgumentNullException("value");

			//if (this.ExpressionCount == 0) throw new InvalidOperationException("Stack empty");

			GhostAssignment assignment = new GhostAssignment(
				settable,
				value,
				new MsilFrameInfo(this._currentFrameStartOffset, this.CurrentOffset)
				);
			this.GhostedAssignmentTable[settable] = assignment;
		}
		public Expression UnghostValue(ISettableExpression settable)
		{
			if (settable == null) throw new ArgumentNullException("settable");

			GhostAssignment assignment;
			Expression value = null;
			if (this.GhostedAssignmentTable.TryGetValue(settable, out assignment))
			{
				this.GhostedAssignmentTable.Remove(settable);
				value = assignment.Value;
			}

			return value;
		}
		private bool CanFlushGhosts
		{
			get { return (this.Statements != null); }
		}
		public void FlushGhostsIfPossible()
		{
			if (this.CanFlushGhosts) this.FlushGhosts();
		}
		public void FlushGhosts()
		{
			if (this.Statements == null) throw new InvalidOperationException(Messages.FlushGhostsInvalidMessage);

			var ghosts = this.GhostedAssignmentTable.ToArray();
			foreach (var pair in ghosts)
			{
				GhostAssignment assignment = pair.Value;
				BoundVariableReferenceExpression variableRef = assignment.Settable as BoundVariableReferenceExpression;
				LocalVariable localVariable = ((variableRef == null) ? null : variableRef.Variable as LocalVariable);
				Statement assignmentStatement;
				if (
					(localVariable != null)
					&& !this.DeclaredVariables.Contains(localVariable)
					)
				{
					assignmentStatement = new VariableDeclarationStatement(localVariable) { Initializer = assignment.Value };
					this.DeclaredVariables.Add(localVariable);
				}
				else
				{
					assignmentStatement = new AssignmentStatement((Expression)assignment.Settable, assignment.Value);
				}
				MsilFrameInfo frameInfo = assignment.FrameInfo;
				this.StatementFrameTable[assignmentStatement] = frameInfo;
				this._currentFrameStartOffset = frameInfo.EndOffset;
				this.Statements.Add(assignmentStatement);
				//this.RecordStatement(assignmentStatement, true);
			}
			this.GhostedAssignmentTable.Clear();
		}

		protected Dictionary<LocalVariable, Expression> DecisionValueTable { get; private set; }
		public void RecordDecision(LocalVariable variable, Expression value)
		{
			if (variable == null) throw new ArgumentNullException("variable");
			if (value == null) throw new ArgumentNullException("value");

			this.FlushGhostsIfPossible();
			this.DecisionValueTable[variable] = value;
		}
		public Expression GetDecision(LocalVariable variable)
		{
			if (variable == null) throw new ArgumentNullException("variable");
			Expression value;
			this.DecisionValueTable.TryGetValue(variable, out value);
			return value;
		}

		public Dictionary<LocalVariable, TypedVariable> LinqParameterTable { get; private set; }

		public Dictionary<FieldInfo, Expression> AnonymousMethodDelegateTable { get; private set; }
		#endregion

		#region Blocks
		public IEnumerable<int> BlockStartOffsets { get { return this._blockStartOffsets; } }
		private List<int> _blockStartOffsets;
		public void MarkBlockStartOffset(int offset)
		{
			if (!this._blockStartOffsets.Contains(offset)) this._blockStartOffsets.Add(offset);
		}

		public bool IsBlockStart()
		{
			return this.BlockStartOffsets.Contains(this.CurrentOffset);
		}
		public int GetBlockStartBefore(int offset)
		{
			return this.BlockStartOffsets.Where(o => o <= offset).Max();
		}
		public int GetBlockEnd(int offset)
		{
			int[] offsets = this.BlockStartOffsets.Where(o => o > offset).ToArray();

			return (offsets.Length == 0) ? this.MethodLength : offsets.Min();
		}

		public List<MsilBlock> Blocks { get; private set; }
		//public MsilBlock CurrentBlock { get; private set; }

		public int? GetLongestTryBlockAt(int tryOffset, int tryEndOffsetLimit)
		{
			var tryBlockLengths = this.MethodBody.ExceptionHandlingClauses.Where(c => (c.TryOffset == tryOffset) && (c.TryLength < (tryEndOffsetLimit - c.TryOffset))).Select(c => c.TryLength).ToArray();

			int? longest = (tryBlockLengths.Length == 0) ? (int?)null : tryBlockLengths.Max();

			return longest;
		}
		public IEnumerable<ExceptionHandlingClause> GetExceptionHandlingClauses(int tryOffset, int tryLength)
		{
			var q = this.MethodBody.ExceptionHandlingClauses.Where(c => (c.TryOffset == tryOffset) && (c.TryLength == tryLength)).ToArray();
			return q;
		}

		public MsilBlock GetBlockAtOffset(int startOffset)
		{
			foreach (var block in this.Blocks)
			{
				if (block.StartOffset == startOffset) return block;
			}

			return null;
		}
		public MsilBlock GetBlockContainingOffset(int offset)
		{
			foreach (var block in this.Blocks)
			{
				if (
					(block.StartOffset <= offset)
					&& (block.EndOffset > offset)
					)
				{
					return block;
				}
			}

			return null;
		}
		//private void SplitBlock(
		//    MsilBlock block,
		//    int splitOffset,
		//    out MsilBlock preceedingBlock,
		//    out MsilBlock followingBlock
		//    )
		//{
		//    if (block == null) throw new ArgumentNullException("block");

		//    preceedingBlock = new MsilBlock(block.StartOffset) { EndOffset = splitOffset };
		//    followingBlock = new MsilBlock(splitOffset) { EndOffset = block.EndOffset };
		//    foreach (var statementFrame in block.StatementFrames)
		//    {
		//        MsilBlock containingBlock = (statementFrame.MsilStart < splitOffset) ? preceedingBlock : followingBlock;
		//        containingBlock.StatementFrames.Add(statementFrame);
		//    }

		//    MsilTransition fallthroughTransition = new MsilTransition(preceedingBlock, MsilTransitionType.Fallthrough, followingBlock.StartOffset);

		//    foreach (var transition in block.EntryTransitions)
		//    {
		//        transition.SourceBlock = preceedingBlock;
		//        preceedingBlock.EntryTransitions.Add(transition);
		//    }
		//    preceedingBlock.ExitTransitions.Add(fallthroughTransition);

		//    followingBlock.EntryTransitions.Add(fallthroughTransition);
		//    foreach (var transition in block.ExitTransitions)
		//    {
		//        transition.SourceBlock = followingBlock;
		//        followingBlock.ExitTransitions.Add(transition);
		//    }
		//}
		#endregion

		#region Transitions
		public List<MsilTransition> Transitions { get; private set; }

		public IEnumerable<MsilBranch> FindBranchesTo(int offset)
		{
			return this.Transitions
				.OfType<MsilBranch>()
				.Where(branch => (branch.TargetOffset == offset));
		}
		public IEnumerable<MsilBranch> FindLoopBranches(int fromAfter, int toBefore)
		{
			return this.Transitions
				.OfType<MsilBranch>()
				.Where(branch =>
					(branch.SourceOffset >= fromAfter)
					&& (branch.TargetOffset <= toBefore)
					)
				;
		}
		public MsilTransition GetTransitionAt(int offset)
		{
			return this.Transitions.SingleOrDefault(transition => (transition.SourceOffset == offset));
		}
		public MsilTransition GetTransitionAfter(int offset)
		{
			int? nextBlockStart = this.GetBlockEnd(offset);
			if (!nextBlockStart.HasValue) return null;

			Instruction instruction = this.GetInstructionJustBefore(nextBlockStart.Value);
			MsilTransition transition = this.GetTransitionAt(instruction.Offset);

			return transition;
		}
		public MsilTransition GetTransitionJustBefore(int offset)
		{
			if (offset == 0) return null;

			Instruction instruction = this.GetInstructionJustBefore(offset);
			MsilTransition transition = this.GetTransitionAt(instruction.Offset);
			return transition;
		}
		#endregion
		#region Labels
		public IDictionary<int, CodeLabel> LabelTable { get; private set; }
		private CodeLabel CreateLabel(int key)
		{
			return new CodeLabel(string.Format("Label_{0:X4}", key));
		}
		public CodeLabel GetLabel(int offset)
		{
			return this.LabelTable[offset];
		}
		#endregion
		#region Expression handling
		protected Stack<MsilStackEntry> ExpressionStack { get; private set; }

		public Expression PopExpression()
		{
			//this.PopCount++;
			return this.ExpressionStack.Pop().Expression;
		}
		public MsilStackEntry PopStackEntry()
		{
			//this.PopCount++;
			return this.ExpressionStack.Pop();
		}
		public Expression PeekExpression()
		{
			//this.PopCount++;
			return this.ExpressionStack.Peek().Expression;
		}
		public MsilStackEntry PeekStackEntry(int depth)
		{
			MsilStackEntry stackEntry = this.ExpressionStack.ToArray()[depth];
			return stackEntry;
		}
		public Expression PeekExpression(int depth)
		{
			return this.PeekStackEntry(depth).Expression;
		}
		public void PushStackEntry(MsilStackEntry stackEntry)
		{
			//this.PushCount++;
			this.ExpressionStack.Push(stackEntry);
		}
		public void PushStackEntry(Expression expression, Type expressionType)
		{
			if (expression == null) throw new ArgumentNullException("expression");
			if (expressionType == null) throw new ArgumentNullException("expressionType");

			//this.PushCount++;
			this.PushStackEntry(new MsilStackEntry(expression, expressionType));
		}
		public void ClearExpressions()
		{
			//this.PopCount += this.CurrentBlock.ExpressionStack.Count;
			this.ExpressionStack.Clear();
		}
		public int ExpressionCount { get { return this.ExpressionStack.Count; } }
		#endregion

		#region Branching
		//private void OnTransition(MsilTransition transition)
		//{
		//    if (transition == null) throw new ArgumentNullException("transition");

		//    this.Transitions.Add(transition);
		//    if (transition.TargetOffset <= this.Offset)
		//    {
		//        MsilBlock targetBlock = this.GetBlockAtOffset(transition.TargetOffset);
		//        if (targetBlock == null)
		//        {
		//            //targetBlock = this.GetBlockContainingOffset(transition.TargetOffset);
		//            //MsilBlock preceedingBlock;
		//            //MsilBlock followingBlock;
		//            //SplitBlock(targetBlock, transition.TargetOffset, out preceedingBlock, out followingBlock);

		//            //int blockIndex = this.Blocks.IndexOf(targetBlock);
		//            //this.Blocks.Insert(blockIndex, preceedingBlock);
		//            //this.Blocks.Insert(blockIndex + 1, followingBlock);
		//            //this.Blocks.Remove(targetBlock);

		//            //if (this.CurrentBlock == targetBlock)
		//            //{
		//            //    this.CurrentBlock = followingBlock;
		//            //}

		//            //targetBlock = followingBlock;
		//        }
		//        targetBlock.EntryTransitions.Add(transition);
		//    }
		//}

		//public void Branch(int targetOffset)
		//{
		//    MsilTransition transition = new MsilTransition(this.Offset, this.CurrentBlock, MsilTransitionType.Branch, targetOffset);
		//    this.CurrentBlock.ExitTransitions.Add(transition);
		//    this.CloseCurrentBlock();

		//    this.OnTransition(transition);
		//}
		//public void ConditionalBranch(int targetOffset, bool criterion)
		//{
		//    Expression condition = this.PopExpression();
		//    MsilTransition branchTransition = new MsilTransition(this.Offset, this.CurrentBlock, MsilTransitionType.Branch, targetOffset, condition, criterion);
		//    MsilTransition fallthroughTransition = new MsilTransition(this.Offset, this.CurrentBlock, MsilTransitionType.Fallthrough, this.NextOffset, condition, !criterion);
		//    this.CurrentBlock.ExitTransitions.Add(branchTransition);
		//    this.CurrentBlock.ExitTransitions.Add(fallthroughTransition);
		//    this.CloseCurrentBlock();

		//    this.OnTransition(fallthroughTransition);
		//    this.OnTransition(branchTransition);
		//}
		#endregion

		#region Framing
		//public int Offset { get; private set; }
		//public int NextOffset { get; private set; }

		//public int PopCount { get; private set; }
		//public int PushCount { get; private set; }

		//public void Advance(int bytes)
		//{
		//    bool isBlockStart =
		//        (this.CurrentBlock == null)
		//        || this.BranchTargetOffsets.Contains(this.NextOffset)
		//        ;
		//    if (isBlockStart)
		//    {
		//        var targetTransitions = this.Transitions.FindAll(transition => transition.TargetOffset == this.NextOffset);
		//        if (this.CurrentBlock != null)
		//        {
		//            MsilTransition fallthroughTransition = new MsilTransition(this.Offset, this.CurrentBlock, MsilTransitionType.Fallthrough, this.NextOffset);
		//            this.CurrentBlock.ExitTransitions.Add(fallthroughTransition);
		//            targetTransitions.Insert(0, fallthroughTransition);
		//        }

		//        this.OpenNewBlock();

		//        // Prepare stack
		//        if (this.ExceptionHandlerOffsets.Contains(this.NextOffset))
		//        {
		//            this.PushExpression(new ExceptionExpression());
		//        }
		//        else if (targetTransitions.Count > 0)
		//        {
		//            this.CurrentBlock.EntryTransitions.AddRange(targetTransitions);

		//            MsilBlock someBlock = targetTransitions[0].SourceBlock;
		//            if (someBlock.ExpressionStack.Count > 0)
		//            {
		//                Stack<MsilTransition> transitionStack = new Stack<MsilTransition>(targetTransitions);
		//                // We have have a conditional expression
		//                SwitchExpression switchExpression = this.FindSwitch(transitionStack.Pop().SourceBlock, transitionStack.Pop().SourceBlock);

		//                // Bypass stack tracking
		//                this.CurrentBlock.ExpressionStack.Clear();
		//                this.PushExpression(switchExpression);
		//            }
		//        }

		//        //foreach (var transition in targetTransitions) this.FutureTransitions.Remove(transition);
		//    }

		//    this.Offset = this.NextOffset;
		//    this.NextOffset += bytes;

		//    this.PopCount = 0;
		//    this.PushCount = 0;
		//}
		#endregion

		#region Statement handling
		public int StatementCount { get; private set; }

		//private MsilFlowAnalyzer _currentFlow;
		//public MsilFlowAnalyzer CurrentFlow
		//{
		//    get { return this._currentFlow; }
		//    set
		//    {
		//        this._currentFlow = value;
		//    }
		//}

		//private IList<Statement> _statements;
		//public IList<Statement> Statements
		//{
		//    get { return this._statements; }
		//    private set
		//    {
		//        if (
		//            (this._statements != null)
		//            && (value != this._statements)
		//            )
		//        {
		//            this.FlushGhosts();
		//        }
		//        this._statements = value;
		//    }
		//}
		public IList<Statement> Statements { get { return this.CurrentFlow.ActiveStatementBlock; } }

		private FlowAnalyzer _currentFlow;
		public FlowAnalyzer CurrentFlow
		{
			get { return this._currentFlow; }
			set
			{
				//if (this._currentFlow != null)
				//{
				//    this._currentFlow.ActiveStatementBlockChanged -= new EventHandler(CurrentFlow_ActiveStatementBlockChanged);
				//}
				this._currentFlow = value;
				//if (value != null)
				//{
				//    value.ActiveStatementBlockChanged += new EventHandler(CurrentFlow_ActiveStatementBlockChanged);
				//}
				//this.Statements =
				//    (value != null)
				//    ? value.ActiveStatementBlock
				//    : null
				//    ;
			}
		}

		//void CurrentFlow_ActiveStatementBlockChanged(object sender, EventArgs e)
		//{
		//    this.Statements =
		//        (this.CurrentFlow != null)
		//        ? this.CurrentFlow.ActiveStatementBlock
		//        : null
		//        ;
		//}

		//protected int StatementFrameOffset { get; private set; }
		public List<LocalVariable> StatementLocalVariableReferences { get; private set; }

		private void FlushVariableDeclarations()
		{
			foreach (var localVariable in this.StatementLocalVariableReferences)
			{
				if (!this.DeclaredVariables.Contains(localVariable))
				{
					VariableDeclarationStatement declaration = new VariableDeclarationStatement(localVariable);
					this.Statements.Add(declaration);
					this.DeclaredVariables.Add(localVariable);
				}
			}
			this.StatementLocalVariableReferences.Clear();
		}

		private int _currentFrameStartOffset;
		public Dictionary<Statement, MsilFrameInfo> StatementFrameTable { get; private set; }

		public void RecordStatement(Statement statement, bool ignoreGhosts = false, MsilFrameInfo frameInfo = null)
		{
			if (statement == null) throw new ArgumentNullException("statement");

			this.FlushVariableDeclarations();

			if (!ignoreGhosts && this.GhostedAssignmentTable.Count > 0)
			{
				FlushGhosts();
			}

			//if (this.ExpressionStack.Count != 0) throw new InvalidOperationException("Expression stack must be empty");
			if (this.Statements == null) throw new InvalidOperationException("No active statement block");

			this.StatementCount++;

			if (frameInfo == null)
			{
				int endOffset = this.CurrentInstruction.NextOffset;
				frameInfo = new MsilFrameInfo(this._currentFrameStartOffset, endOffset);
				this.StatementFrameTable[statement] = frameInfo;
			}

			this._currentFrameStartOffset = frameInfo.EndOffset;

			this.Statements.Add(statement);
			//StatementFrame frame = new StatementFrame(statement, this.StatementFrameOffset, this.CurrentRecord.NextOffset);
			//frame.VariableReferences.AddRange(this.StatementVariableReferences);
			//this.StatementVariableReferences.Clear();

			//this.CurrentBlock.StatementFrames.Add(frame);

			//this.StatementFrameOffset = this.CurrentRecord.NextOffset;
		}

		//public StatementFrame GetStatementFrameAtOffset(int msilOffset)
		//{
		//    StatementFrame frame;
		//    this.StatementFrameTable.TryGetValue(msilOffset, out frame);

		//    return frame;
		//}
		//public StatementFrame GetStatementFrameBefore(StatementFrame statementFrame)
		//{
		//    if (statementFrame == null) throw new ArgumentNullException("statementFrame");
		//    int index = this.StatementFrames.IndexOf(statementFrame);
		//    if (index <= 0) throw new ArgumentException("The statement frame does not occur in this context");

		//    statementFrame = this.StatementFrames[index - 1];

		//    return statementFrame;
		//}
		//public StatementFrame GetStatementFrameAfter(StatementFrame statementFrame)
		//{
		//    if (statementFrame == null) throw new ArgumentNullException("statementFrame");
		//    int index = this.StatementFrames.IndexOf(statementFrame);
		//    if (index <= 0) throw new ArgumentException("The statement frame does not occur in this context");

		//    statementFrame = this.StatementFrames[index + 1];

		//    return statementFrame;
		//}
		#endregion

		#region Opcode Handling
		#region Variables
		//public Dictionary<Variable, Expression> VariableAssignments { get; private set; }
		//public List<Variable> DeclaredVariables { get; private set; }

		protected string GenerateVariableNameHintFromType(Type type)
		{
			if (this.Equal(type, typeof(string)))
			{
				return "text";
			}
			else if (
				this.Equal(type, typeof(int))
				|| this.Equal(type, typeof(short))
				|| this.Equal(type, typeof(byte))
				|| this.Equal(type, typeof(sbyte))
				|| this.Equal(type, typeof(uint))
				|| this.Equal(type, typeof(ushort))
				|| this.Equal(type, typeof(long))
				|| this.Equal(type, typeof(ulong))
				|| this.Equal(type, typeof(float))
				|| this.Equal(type, typeof(double))
				|| this.Equal(type, typeof(decimal))
				)
			{
				return "num";
			}
			else if (this.Equal(type, typeof(bool)))
			{
				return "flag";
			}
			else
			{
				char[] nameChars = type.Name.ToCharArray();
				nameChars[0] = char.ToLower(nameChars[0]);
				return new string(nameChars);
			}
		}
		protected string GenerateVariableName(Type type, string hint)
		{
			if (hint == null)
			{
				hint = GenerateVariableNameHintFromType(type);
			}

			int i = 1;
			bool exists = false;
			string newName;
			do
			{
				exists = false;
				newName = hint + i;
				foreach (var variable in this.LocalVariables)
				{
					if (variable.Name == newName)
					{
						exists = true;
						i++;
						break;
					}
				}
			} while (exists);

			return newName;
		}
		#endregion
		#region Operators
		#endregion

		#endregion

		//public void AnalyzeBranches()
		//{
		//    int index = 0;
		//    while (index >= 0)
		//    {
		//        index = AnalyzeBranches(this.StatementRecords, index);
		//    }
		//}
		//public static int AnalyzeBranches(List<Record<Statement>> statementRecords, int startOffset)
		//{
		//    int branchIndex = statementRecords.FindIndex(startOffset, r => (r.Object is BranchStatement));
		//    if (branchIndex >= 0)
		//    {
		//        Record<Statement> branchRecord = statementRecords[branchIndex];
		//        if (branchRecord != null)
		//        {
		//            BranchStatement branch = (BranchStatement)branchRecord.Object;
		//            ConditionalBranchStatement conditionalBranch = branch as ConditionalBranchStatement;
		//            MsilLabel branchTarget = (MsilLabel)branch.Target;
		//            if ((conditionalBranch != null) && (branchTarget.Offset < branchRecord.Offset))
		//            {
		//                // This is a DO loop
		//                // Find initial statement
		//                int loopStartIndex = statementRecords.FindIndex(0, r => (r.Offset == branchTarget.Offset));
		//                LoopStatement loop = new LoopStatement(conditionalBranch.Condition);
		//                int loopExitOffset = -1;
		//                int loopExitIndex = branchIndex + 1;
		//                if (statementRecords.Count > loopExitIndex)
		//                {
		//                    loopExitOffset = statementRecords[loopExitIndex].Offset;
		//                }

		//                int loopStatementRecordCount = branchIndex - loopStartIndex;
		//                foreach (var statementRecord in statementRecords.Skip(loopStartIndex).Take(loopStatementRecordCount))
		//                {
		//                    loop.Statements.Add(statementRecord.Object);
		//                }
		//                statementRecords.RemoveRange(loopStartIndex, loopStatementRecordCount + 1);
		//                statementRecords.Insert(loopStartIndex, new Record<Statement>(branchRecord.Offset, loop));
		//            }
		//        }
		//    }

		//    return (branchIndex == -1) ? -1 : branchIndex + 1;
		//}

		//private LoopInfo TryGetWhileLoopInfo(
		//    int enterMsilOffset
		//    )
		//{
		//    StatementFrame enterStatementFrame = this.GetStatementFrameAtOffset(enterMsilOffset);
		//    if (enterStatementFrame == null)
		//        throw new ArgumentNullException("Invalid MSIL offset");

		//    BranchStatement enterBranchStatement = enterStatementFrame.Statement as BranchStatement;
		//    if (
		//        (enterBranchStatement == null)
		//        || (enterBranchStatement is ConditionalBranchStatement)
		//        )
		//        return null;

		//    int loopBodyMsilOffset = enterStatementFrame.NextMsilOffset;
		//    int continueMsilOffset = this.GetLabelOffset(enterBranchStatement.Target).Value;
		//    StatementFrame continueFrame = this.GetStatementFrameAtOffset(continueMsilOffset);
		//    if (
		//        (continueFrame != null)
		//        )
		//    {
		//        ConditionalBranchStatement continueBranchStatement = continueFrame.Statement as ConditionalBranchStatement;
		//        bool isWhileLoop = false;
		//        int? exitOffset = null;
		//        if (continueBranchStatement != null)
		//        {
		//            exitOffset = continueFrame.NextMsilOffset;
		//        }
		//        else
		//        {
		//            AssignmentStatement continueAssignmentStatement = continueFrame.Statement as AssignmentStatement;
		//            if (continueAssignmentStatement != null)
		//            {
		//                continueFrame = this.GetStatementFrameAfter(continueFrame);
		//                continueBranchStatement = continueFrame.Statement as ConditionalBranchStatement;
		//                if (continueBranchStatement != null)
		//                {
		//                    exitOffset = continueFrame.NextMsilOffset;
		//                }
		//            }
		//        }

		//        if (continueBranchStatement != null)
		//        {
		//            int continueTargetOffset = this.GetLabelOffset(continueBranchStatement.Target).Value;

		//            // Ensure that the supposed continue statement branches to the loop body
		//            if (continueTargetOffset == loopBodyMsilOffset)
		//            {
		//                LoopInfo loopInfo = new LoopInfo(
		//                    this,
		//                    enterMsilOffset,
		//                    loopBodyMsilOffset,
		//                    continueMsilOffset,
		//                    exitOffset.Value
		//                    );
		//                return loopInfo;
		//            }
		//        }
		//    }

		//    return null;
		//}

		//private LoopInfo TryGetDoLoopInfo(
		//    int continueBranchMsilOffset
		//    )
		//{
		//    StatementFrame continueBranchFrame = this.GetStatementFrameAtOffset(continueBranchMsilOffset);
		//    if (continueBranchFrame == null) return null;

		//    ConditionalBranchStatement continueBranchStatement = continueBranchFrame.Statement as ConditionalBranchStatement;
		//    if (continueBranchStatement == null) return null;

		//    int continueTargetOffset = this.GetLabelOffset(continueBranchStatement.Target).Value;
		//    if (continueTargetOffset > continueBranchMsilOffset) return null;

		//    StatementFrame possibleWhileEnterFrame = this.GetStatementFrameBefore(this.GetStatementFrameAtOffset(continueTargetOffset));
		//    bool isWhileLoop = (this.TryGetWhileLoopInfo(possibleWhileEnterFrame.MsilOffset) != null);
		//    if (isWhileLoop) return null;

		//    LoopInfo loopInfo = new LoopInfo(
		//        this,
		//        continueTargetOffset,
		//        continueTargetOffset,
		//        continueBranchMsilOffset,
		//        continueBranchFrame.NextMsilOffset
		//        );

		//    return loopInfo;
		//}

		//private ConditionalBlockInfo TryGetConditionBlockInfo(
		//    int enterMsilOffset
		//    )
		//{
		//    StatementFrame enterBranchFrame = this.GetStatementFrameAtOffset(enterMsilOffset);
		//    if (enterBranchFrame == null) return null;

		//    ConditionalBranchStatement enterBranchStatement = enterBranchFrame.Statement as ConditionalBranchStatement;
		//    if (enterBranchStatement == null) return null;

		//    int enterTargetOffset = this.GetLabelOffset(enterBranchStatement.Target).Value;
		//    if (enterTargetOffset <= enterMsilOffset) return null;

		//    StatementFrame elseFrame = this.GetStatementFrameAtOffset(enterTargetOffset);
		//    StatementFrame lastBodyFrame = this.GetStatementFrameBefore(elseFrame);
		//    BranchStatement lastBodyBranch = lastBodyFrame.Statement as BranchStatement;

		//    int exitOffset =
		//        (lastBodyBranch != null)
		//        ? this.GetLabelOffset(lastBodyBranch.Target).Value
		//        : enterTargetOffset;

		//    ConditionalBlockInfo info = new ConditionalBlockInfo(
		//        this,
		//        enterMsilOffset,
		//        enterBranchFrame.NextMsilOffset,
		//        enterTargetOffset,
		//        exitOffset
		//        );
		//    return info;
		//}

		//public IEnumerable<CodeBlockInfo> AnalyzeBlocks()
		//{
		//    List<CodeBlockInfo> blocks = new List<CodeBlockInfo>();
		//    foreach (var statementFrame in this.StatementFrames)
		//    {
		//        CodeBlockInfo blockInfo = null;
		//        if (blockInfo == null) blockInfo = this.TryGetWhileLoopInfo(statementFrame.MsilOffset);
		//        if (blockInfo == null) blockInfo = this.TryGetDoLoopInfo(statementFrame.MsilOffset);
		//        if (blockInfo == null) blockInfo = this.TryGetConditionBlockInfo(statementFrame.MsilOffset);

		//        if (blockInfo != null)
		//        {
		//            blocks.Add(blockInfo);
		//        }
		//    }

		//    return blocks;
		//}
	}
}
